package org.koin.experimental.builder;

import org.koin.core.definition.BeanDefinition;
import org.koin.core.definition.Definition;
import org.koin.core.error.*;
import org.koin.core.parameter.DefinitionParameters;
import org.koin.core.qualifier.Qualifier;
import org.koin.core.scope.Scope;
import org.koin.dsl.*;

public class ScopeSetExt {

    /**
     * Create a Single definition for given type T
     *
     * @param clazz
     * @param qual
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */

    public static <T> BeanDefinition<T> scoped(Class<T> clazz, Qualifier qual, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        Object[] obj = new Object[1];
        KoinApplication.koinApplication(new KoinAppDeclaration() {
            @Override
            public void invoke(org.koin.core.KoinApplication koinApplication) {
                try {
                    koinApplication.modules(Module.module(new ModuleDeclaration() {
                        @Override
                        public void invoke(org.koin.core.module.Module module) {
                            module.scope(clazz, new org.koin.core.module.Module.ScopeSet() {
                                @Override
                                public void invoke(ScopeDSL scopeDSL) {
                                    try {
                                        obj[0] = scopeDSL.scoped(clazz, qual, ovrride,
                                                new Definition<T>() {
                                            @Override
                                            public T invoke(Scope scope, DefinitionParameters
                                                    parameters) {
                                                try {
                                                    return InstanceBuilder.create(scope, clazz);
                                                } catch (DefinitionParameterException e) {
                                                    e.printStackTrace();
                                                } catch (NoBeanDefFoundException e) {
                                                    e.printStackTrace();
                                                } catch (ClosedScopeException e) {
                                                    e.printStackTrace();
                                                }
                                                return null;
                                            }
                                        });
                                    } catch (DefinitionOverrideException e) {
                                        e.printStackTrace();
                                    }
                                }
                            });
                        }
                    }));
                } catch (DefinitionOverrideException e) {
                    e.printStackTrace();
                }
            }
        });
        return (BeanDefinition<T>) obj[0];
    }

    /**
     * Create a Single definition for given type T
     *
     * @param clazz
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */
    public static <T> BeanDefinition<T> scoped(Class<T> clazz, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        return scoped(clazz, null, ovrride);
    }

    /**
     * create a Factory definition for given type T
     *
     * @param clazz
     * @param qual
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */

    public static <T> BeanDefinition<T> factory(Class<T> clazz, Qualifier qual, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {

        Object[] obj = new Object[1];
        KoinApplication.koinApplication(new KoinAppDeclaration() {
            @Override
            public void invoke(org.koin.core.KoinApplication koinApplication) {
                try {
                    koinApplication.modules(Module.module(new ModuleDeclaration() {
                        @Override
                        public void invoke(org.koin.core.module.Module module) {
                            try {
                                obj[0] = module.factory(clazz, qual, ovrride, new Definition<T>() {
                                    @Override
                                    public T invoke(Scope scope, DefinitionParameters parameters) {
                                        try {
                                            return InstanceBuilder.create(scope, clazz);
                                        } catch (DefinitionParameterException e) {
                                            e.printStackTrace();
                                        } catch (NoBeanDefFoundException e) {
                                            e.printStackTrace();
                                        } catch (ClosedScopeException e) {
                                            e.printStackTrace();
                                        }
                                        return null;
                                    }
                                });
                            } catch (DefinitionOverrideException e) {
                                e.printStackTrace();
                            }
                        }
                    }));
                } catch (DefinitionOverrideException e) {
                    e.printStackTrace();
                }
            }
        });
        return (BeanDefinition<T>) obj[0];
    }

    /**
     * create a Factory definition for given type T
     *
     * @param clazz
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */
    public static <T> BeanDefinition<T> factory(Class<T> clazz, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        return factory(clazz, null, ovrride);
    }


    /**
     * Create a Single definition for given type T to modules and cast as R
     *
     * @param clazz
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */
    public static <T> BeanDefinition<T> scopedBy(Class<T> clazz, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        return scopedBy(clazz, null, ovrride);
    }

    /***
     * Create a Single definition for given type T to modules and cast as R
     * @param clazz
     * @param qual
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */
    public static <T> BeanDefinition<T> scopedBy(Class<T> clazz, Qualifier qual, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        final Object[] obj = new Object[1];
        KoinApplication.koinApplication(new KoinAppDeclaration() {
            @Override
            public void invoke(org.koin.core.KoinApplication koinApplication) {
                org.koin.core.module.Module module = Module.module(new ModuleDeclaration() {
                    @Override
                    public void invoke(org.koin.core.module.Module module) {
                        try {
                            module.scope(clazz, new org.koin.core.module.Module.ScopeSet() {
                                @Override
                                public void invoke(ScopeDSL scopeDSL) {
                                    try {
                                        obj[0] = scopeDSL.scoped(clazz, qual, ovrride,
                                                new Definition<T>() {
                                            @Override
                                            public T invoke(Scope scope, DefinitionParameters
                                                    parameters) {
                                                try {
                                                    return InstanceBuilder.create(scope, clazz);
                                                } catch (DefinitionParameterException e) {
                                                    e.printStackTrace();
                                                } catch (NoBeanDefFoundException e) {
                                                    e.printStackTrace();
                                                } catch (ClosedScopeException e) {
                                                    e.printStackTrace();
                                                }
                                                return null;
                                            }
                                        });
                                    } catch (DefinitionOverrideException e) {
                                        e.printStackTrace();
                                    }
                                }
                            });
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
                try {
                    koinApplication.modules(module);
                } catch (DefinitionOverrideException e) {
                    e.printStackTrace();
                }
            }
        });
        return (BeanDefinition<T>) obj[0];
    }

    /***
     *  Create a Factory definition for given type T to modules and cast as R
     *
     * @param clazz
     * @param qual
     * @param ovrride
     * @param <T>
     * @return
     */
    public static <T> BeanDefinition<T> factoryBy(Class<T> clazz, Qualifier qual, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        Object[] obj = new Object[1];
        KoinApplication.koinApplication(new KoinAppDeclaration() {
            @Override
            public void invoke(org.koin.core.KoinApplication koinApplication) {
                try {
                    koinApplication.modules(Module.module(new ModuleDeclaration() {
                        @Override
                        public void invoke(org.koin.core.module.Module module) {
                            try {
                                obj[0] = module.factory(clazz, qual, ovrride, new Definition<T>() {
                                    @Override
                                    public T invoke(Scope scope, DefinitionParameters parameters) {
                                        try {
                                            return InstanceBuilder.create(scope, clazz);
                                        } catch (DefinitionParameterException e) {
                                            e.printStackTrace();
                                        } catch (NoBeanDefFoundException e) {
                                            e.printStackTrace();
                                        } catch (ClosedScopeException e) {
                                            e.printStackTrace();
                                        }
                                        return null;
                                    }
                                });
                            } catch (DefinitionOverrideException e) {
                                e.printStackTrace();
                            }
                        }
                    }));
                } catch (DefinitionOverrideException e) {
                    e.printStackTrace();
                }
            }
        });
        return (BeanDefinition<T>) obj[0];
    }

    /**
     * Create a Factory definition for given type T to modules and cast as R
     *
     * @param clazz
     * @param ovrride
     * @param <T>
     * @return
     * @throws ScopeAlreadyCreatedException
     * @throws NoScopeDefFoundException
     */
    public static <T> BeanDefinition<T> factoryBy(Class<T> clazz, Boolean ovrride)
            throws ScopeAlreadyCreatedException, NoScopeDefFoundException {
        return factoryBy(clazz, null, ovrride);
    }
}
